home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / packet / p_g8bpq / bpq_misc / chat4.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1991-01-04  |  17.3 KB  |  748 lines

  1. Program  Chat4;
  2. {$M 4096,0,2000}
  3.         { Host Mode CHAT SERVER Version 2.0 by Martin Stubbs G8IMB }
  4.         { Version 2 written specifically for BPQ version 4         }
  5.  
  6. Uses Crt,Dos;
  7.  
  8. const
  9.   CR     = #$0D;
  10.   LF     = #$0A;
  11.   CRLF   = CR+LF;
  12.   SOH    = $01;
  13.   DLE    = $10;
  14.   ETB    = $17;
  15.  
  16. type
  17.   line   = string[80];
  18.   User_rec = record
  19.                User_call : String[6];
  20.                User_name : String[10];
  21.               end;
  22.  
  23. var
  24.   Ch         : Char;
  25.   err        : Integer;
  26.   Logged_in  : Array [0..10] of Boolean;     { Is someone on this channel }
  27.   Callsign   : Array [0..10] of String[10];  { Connected callsign }
  28.   Conf       : Array [0..10] of byte;        { Which conference }
  29.   Name       : Array [0..10] of String[10];  { Users name       }
  30.   I          : integer;
  31.   p          : Integer;
  32.   Start_port : Integer;
  33.   No_ports   : Integer;
  34.   resp_len   : Integer;
  35.  
  36.   Quit       : Boolean;
  37.   xloc,yloc  : Integer;
  38.   xkeep,ykeep: Integer;
  39.   Welcome_st : String[80];
  40.  
  41.   Regs       : Registers;
  42.   Cnf        : text;
  43.   Log        : text;
  44.   Users      : File of User_rec;
  45.   Use_data   : User_rec;
  46.   Appl       : Byte;
  47.   Link_appl  : Byte;
  48.   Linking    : Boolean;
  49.   Net_connect: Boolean;
  50.   Link_port  : Byte;
  51.   Out_port   : Byte;
  52.   Link_call  : String[10];
  53.  
  54.   BPQbuff    : Array [1..255] of byte;
  55.   OBuffer    : String[255];
  56.   IBuffer    : String[255];
  57.   locbuff    : String[255];
  58.  
  59. Procedure Logout(n:Integer);Forward;  { Forward declarations of procedures }
  60. Procedure Login (n:Integer);Forward;
  61.  
  62. procedure DV_Nice;          {Give time slice to next task}
  63.   begin
  64.     regs.ax := $1000;
  65.     Intr($15, regs);
  66.   end;
  67.  
  68. Procedure Display(St:String);
  69. Begin
  70.  
  71.   Window(1,5,70,21);
  72.  
  73.   GotoXY(xkeep,ykeep);
  74.   Write(St);
  75.   xkeep := WhereX;
  76.   ykeep := WhereY;
  77.  
  78.   Window(1,23,80,23);
  79.   GoToXy(Xloc,Yloc);
  80. End;
  81.  
  82. Function Time:String;
  83. Var
  84.   X : Word;
  85.   I : Integer;
  86.   Timarr: Array[1..6] of word;
  87.   Timst : Array[1..6] of string[4];
  88.  
  89. Begin
  90.   GetDate(Timarr[3],Timarr[2],Timarr[1],x);
  91.   GetTime(Timarr[4],Timarr[5],Timarr[6],x);
  92.  
  93.   For I := 1 to 6 do
  94.   Begin
  95.     Str(Timarr[I]:2,Timst[I]);
  96.   End;
  97.  
  98.   Time := timst[1]+'/'+timst[2]+'/'+timst[3]+'  '+
  99.           timst[4]+':'+timst[5]+':'+timst[6];
  100. End;
  101.  
  102. Function Poll(p:Integer):Boolean;
  103. Var
  104.   Change : Boolean;
  105.  
  106. Begin
  107.   Change := False;
  108.  
  109.   regs.ah := $04;
  110.   regs.al := p;
  111.   intr($7F,regs);
  112.  
  113.   If regs.dx = 1 then Change := True;
  114.  
  115.   regs.ah := $05;
  116.   regs.al := p;
  117.   intr($7F,regs);
  118.  
  119.   If Change then Poll := True
  120.             else Poll := False;
  121.  
  122. End;
  123.  
  124. Function Get_resp(p:Integer):Boolean;
  125. Var
  126.   I    : Integer;
  127.   pass : Boolean;
  128.  
  129. Begin
  130.  
  131.   regs.di := Ofs(BPQbuff);
  132.   regs.es := Seg(BPQbuff);
  133.   regs.ah := $03;
  134.   regs.al := p;
  135.   intr($7F,regs);
  136.  
  137.   If regs.cx > 0 then
  138.   Begin
  139.     IBuffer := '';
  140.     For I := 1 to regs.cx do
  141.     Begin
  142.       IBuffer := IBuffer + Chr(BPQbuff[I]);
  143.       If BPQbuff[I] = $0D then
  144.          IBuffer := IBuffer + #$0A;
  145.     End;
  146.     Get_resp := True;
  147.   End
  148.   else
  149.     Get_resp := False;
  150. End;
  151.  
  152. Procedure Send(p:Integer);
  153. var
  154.   Inp,Out : Integer;
  155.  
  156. Begin
  157.  
  158.   For Inp := 1 to Length(OBuffer) do
  159.   Begin
  160.     BPQbuff[Inp] := Ord(OBuffer[Inp]);  { Convert char to byte }
  161.   End;
  162.  
  163.   regs.cx := Length(OBuffer);
  164.   regs.si := Ofs(BPQbuff);
  165.   regs.es := Seg(BPQbuff);
  166.   regs.ah := $02;
  167.   regs.al := Start_port + p;
  168.   intr($7F,regs);
  169.  
  170. end;
  171.  
  172. Function BPQ_loaded: Boolean;
  173. Var
  174.   Seg ,ofs  : word;
  175.   Seg1,ofs1 : word;
  176.   I         : integer;
  177.   St        : String[7];
  178.  
  179. Begin
  180.   Seg := 0;
  181.   Ofs := $01FC;                        { Address of Int $7F      }
  182.   Ofs1 := memw[Seg:Ofs];               { Find address of BPQcode }
  183.   Seg1 := memw[Seg:ofs+2];
  184.  
  185.   ofs1 := Ofs1 - 7;
  186.   St := '';
  187.   For I := 0 to 4 do
  188.   Begin
  189.     ofs := Ofs1 + I;
  190.     St := St + Chr(mem[Seg1:Ofs]);     { Read byte from memory }
  191.   End;
  192.  
  193.   BPQ_loaded := (St='G8BPQ');          { Does it match string }
  194.  
  195. End;
  196.  
  197. Procedure Get_Config;
  198. Begin
  199.   Assign(Cnf,'Chat.cnf');
  200.   {$I-}
  201.   Reset(Cnf);
  202.   {$I+}
  203.   If IOresult <> 0 then
  204.   Begin
  205.     WriteLn('Configuration file - CHAT.CNF not found ');
  206.     Halt;
  207.   End;
  208.  
  209.   Read(Cnf,Welcome_st);           { Read 1 line from CNF file }
  210.   Close(Cnf);
  211.  
  212. End;
  213.  
  214.  
  215. Procedure Log_data(St:String);
  216. Begin
  217.   Assign(log,'Chat.log');
  218.   {$I-}
  219.   Append(log);
  220.   {$I+}
  221.   If IOresult <> 0 then
  222.     Rewrite(log);
  223.  
  224.   Write(log,st+' '+Time+CR+LF);
  225.   Close(log);
  226.  
  227. End;
  228.  
  229. Procedure Find_name(p:Integer);
  230. Var
  231.   Match : Boolean;
  232.  
  233. Begin
  234.   Match := False;
  235.   Assign(Users,'Chatuser.dat');
  236.   {$I-}
  237.   Reset(Users);                       { See if user file exists }
  238.   {$I+}
  239.   If IOresult <> 0 then
  240.     Rewrite(Users)                     { Create a new file }
  241.   else
  242.   With Use_data do
  243.   Begin
  244.     While (not match) and (not EOF(Users)) do
  245.     Begin
  246.       Read(Users,Use_data);
  247.       Match := (User_call=Callsign[p]);
  248.     End;
  249.   End; { With Use_data }
  250.  
  251.   If (not match) then
  252.      Name[p] := 'New User'
  253.   else
  254.      Name[p] := Use_data.User_name;
  255.  
  256.   Close(Users);
  257.  
  258. End;
  259.  
  260. Procedure setup;   {read command line}
  261. var
  262.     err: integer;
  263.       i: integer;
  264.       p: integer;
  265.  
  266. begin
  267.   If (ParamCount = 0) then
  268.   Begin
  269.     Display(' You must supply the port number as a parameter ');
  270.     Halt;
  271.   End
  272.   else
  273.   Begin
  274.  
  275.     Val(Paramstr(1),i,err); If (err = 0) then Start_port := i;
  276.     Val(Paramstr(2),i,err); If (err = 0) then No_ports := i;
  277.     Val(Paramstr(3),Appl,err);
  278.  
  279.     If (Start_port<1) or (No_ports>9) or (Start_port+No_ports>64) or
  280.        (Appl = 0) then
  281.     Begin
  282.       Display('Parameter error');
  283.       WriteLn('Start port = ',Start_port);
  284.       WriteLn('Number ports = ',No_ports);
  285.       WriteLn('Appl = ',Appl);
  286.       WriteLn(' Hit Enter to continue ');
  287.       ReadLn;
  288.       Halt;
  289.     end
  290.     else
  291.       Display('Using Ports '+Chr(Start_port+$30)+' to '+
  292.                              Chr(Start_port+$30+No_ports-1)+CRLF);
  293.   End;
  294.  
  295.   Val(Paramstr(4),Link_Appl,err); If (err = 0) then Linking := True
  296.                                                else Linking := False;
  297.  
  298.   Link_port := Start_port + No_ports;
  299.   Out_port := Link_port + 1;
  300.  
  301.   Callsign[10] := 'Sysop';                 { Set default sysop call }
  302.   Conf[10] := 0;
  303.  
  304.   Window(1,1,80,3);
  305.   WriteLn('   0       1       2       3        4       5       6       7',
  306.           '      8        9');
  307.  
  308.   Log_data('Initialsed');
  309.  
  310.   For I := 0 to No_ports - 1 do
  311.     Logged_In[I] := False;
  312.  
  313.   For I := 0 to No_ports - 1 do
  314.   Begin
  315.     regs.cl := 0;                   { Application mask   }
  316.     regs.dl := Appl;                { Application number }
  317.     regs.ah := $01;
  318.     regs.al := Start_port + I;
  319.     intr($7F,regs);
  320.  
  321.     Callsign[I] := ' ';               { Clear Callsign }
  322.   End;
  323.  
  324.   If Linking then
  325.   Begin
  326.     regs.cl := 0;                   { Application mask   }
  327.     regs.dl := Link_Appl;           { Application number }
  328.     regs.ah := $01;
  329.     regs.al := Link_port;
  330.     intr($7F,regs);
  331.   End;
  332.  
  333. End;
  334.  
  335. Procedure Login(n:integer);
  336. Var
  337.    I : Integer;
  338.    P : Integer;
  339.  
  340. Begin
  341.  
  342.     regs.ah := $08;               { Get callsign }
  343.     regs.al := Start_port + n;
  344.     regs.di := Ofs(BPQbuff);
  345.     regs.es := Seg(BPQbuff);
  346.     intr($7F,regs);
  347.  
  348.     Callsign[n] := '';
  349.  
  350.     I := 1;                        { Strip callsign }
  351.     While (I < 9) and (Chr(BPQbuff[I]) <> '-') and
  352.                       (Chr(BPQbuff[I]) <> ' ') do
  353.     Begin
  354.       Callsign[n] := Callsign[n] + Chr(BPQbuff[I]);
  355.       I := I + 1;
  356.     End;
  357.  
  358.     Display('Call connected '+Callsign[n]+'  Channel no. '+ chr(n+$30)+CRLF);
  359.  
  360.     Find_name(n);
  361.  
  362.     OBuffer := 'Hi ' + name[n] + ' ' + Welcome_st + CR;
  363.     Send(n);
  364.     OBuffer := '/W  will give a list of Who is on.  /H for help' + CR;
  365.     Send(n);
  366.  
  367.     OBuffer := Callsign[n] + '  ' + name[n] + ' has join the group ' + CR;
  368.  
  369.     For I := 0 to No_ports - 1 do
  370.     Begin
  371.       If Logged_in[I] then
  372.       Begin
  373.         Send(I);
  374.       End;
  375.     End;
  376.  
  377.     Logged_in[n] := True;       { Mark that user is logged in }
  378.     Conf[n] := 0;
  379.  
  380.     Log_data(Callsign[n]+' connected');
  381.  
  382.     Window(1,1,80,3);
  383.  
  384.     GotoXY(8*n+1,2); Write(Callsign[n]);
  385.     GotoXY(8*n+1,3); Write(Name[n]);
  386.  
  387.     Window(1,23,80,23);
  388.     GoToXy(Xloc,Yloc);
  389.  
  390. End;
  391.  
  392. Procedure Logout(n:integer);
  393. Var
  394.   I    : Integer;
  395.  
  396. Begin
  397.   logged_in[n] := False;
  398.   OBuffer := Callsign[n] + ' has disconnected ' + CR;
  399.  
  400.   For I := 0 to No_ports - 1 do
  401.   Begin
  402.     If Logged_in[I] then
  403.     Begin
  404.        Send(I);
  405.     End;
  406.   End;
  407.  
  408.   Log_data(Callsign[n]+' disconnected');
  409.  
  410.   Window(1,1,80,3);
  411.  
  412.   GotoXY(8*n+1,2);Write('  DISC ');
  413.   GotoXY(8*n+1,3);Write('       ');
  414.  
  415.   Window(1,23,80,23);
  416.   GoToXy(Xloc,Yloc);
  417.  
  418.   Display('Call disconnected '+Callsign[n]+'  Channel no. '+Chr(n+$30)+CRLF);
  419.  
  420. End;
  421.  
  422. Procedure Link_login;
  423. Begin
  424.   regs.ah := $08;               { Get callsign }
  425.   regs.al := Link_port;
  426.   regs.di := Ofs(BPQbuff);
  427.   regs.es := Seg(BPQbuff);
  428.   intr($7F,regs);
  429.  
  430.   Link_Call := '';
  431.  
  432.   I := 1;                        { Strip callsign }
  433.   While (I < 9) and (Chr(BPQbuff[I]) <> '-') and
  434.                     (Chr(BPQbuff[I]) <> ' ') do
  435.   Begin
  436.     Link_Call := Link_Call + Chr(BPQbuff[I]);
  437.     I := I + 1;
  438.   End;
  439.  
  440.   Window(71,5,80,21);
  441.   GoToXY(1,1); Write(Link_call);
  442.  
  443.   Window(1,23,80,23);
  444.   GoToXy(Xloc,Yloc);
  445.  
  446. End;
  447.  
  448. Procedure Link_Logout;
  449. Begin
  450.  
  451. End;
  452.  
  453. { Procedure SendAll is used to send a user message to the other stations }
  454. {                   who are in his conference                            }
  455.  
  456. Procedure SendAll(n:integer);
  457. Var
  458.   I : Integer;
  459.  
  460. Begin
  461.  
  462.   OBuffer := '[' + callsign[n] + '] ' + IBuffer;
  463.                                { Send to anyone logged on who is in }
  464.                                { the same conference as sender      }
  465.   For I := 0 to No_ports - 1 do
  466.   Begin
  467.     If (Logged_in[I]) and (I <> n) then
  468.       If (Conf[n] = Conf[I]) or (n = 10) then  {send sysop msgs to all }
  469.       Begin
  470.         Send(I);
  471.       End;
  472.   End;
  473.   If conf[n] <> 0 then Write('(',Conf[n],')');  { Tell sysop the conf no. }
  474.   Display(OBuffer);              { Send to local console }
  475.  
  476. End;
  477.  
  478. { Procedure Shut_down is used to close down the node gracefully          }
  479.  
  480. Procedure Shut_down;
  481. Var
  482.   I : Integer;
  483.  
  484. Begin
  485.   For I := 0 to No_ports - 1 do
  486.   Begin
  487.     If Logged_in[I] then
  488.     Begin
  489.        IBuffer := 'Sorry .. Chat Node is closing down for a while ';
  490.        SendAll(10);                   { Use IBuffer cos of SendAll }
  491.        Delay(2000);                   { Wait for message to get there }
  492.  
  493.        regs.cx := 2;                  { Disconnect stream }
  494.        regs.ah := $06;
  495.        regs.al := Start_port + I;
  496.        intr($7F,regs);
  497.      End;
  498.   End;
  499. End;
  500.  
  501. Procedure Command(p:integer);
  502. Var
  503.   Comm_let : Char;
  504.   Sbit,Cbit: String[2];
  505.   Match    : boolean;
  506.  
  507. Begin
  508.  
  509.   Comm_let := IBuffer[2];
  510.  
  511.   Case Comm_let of
  512.  
  513.   'b','B'        : Begin
  514.                      OBuffer := 'Thank you for calling ' + name[p] + CR;
  515.                      Send(p);
  516.                      Delay(1000);
  517.  
  518.                      regs.cx := 3;
  519.                      regs.ah := $06;
  520.                      regs.al := Start_port + p;
  521.                      intr($7F,regs);
  522.                    End;
  523.  
  524.  
  525.   'c','C'        : Begin
  526.                      Val(IBuffer[4],conf[p],err);
  527.                      If (Conf[p] > 4) or (err <> 0) then
  528.                      Begin
  529.                        OBuffer := 'Error in conference number' + CR;
  530.                        Send(p);
  531.                        Conf[p] := 0;
  532.                      End
  533.                      Else
  534.                      Begin
  535.                        OBuffer := 'Conference channel has been changed' + CR;
  536.                        Send(p);
  537.                      End;
  538.                    End;
  539.  
  540.   'h','H','?': Begin
  541.                  OBuffer := 'The commands which are available are :-' + CR;
  542.                  Send(p);
  543.                  OBuffer := '/?     - To read this list' + CR;
  544.                  Send(p);
  545.                  OBuffer := '/B     - To leave the chat node' + CR;
  546.                  Send(p);
  547.                  OBuffer := '/C n   - To switch to conference stream n' + CR;
  548.                  Send(p);
  549.                  OBuffer := '/H     - To read this list' + CR;
  550.                  Send(p);
  551.                  OBuffer := '/N Yourname - To register onto the node' + CR;
  552.                  Send(p);
  553.                  OBuffer := '/Q     - To disconnect from the node completely' + CR;
  554.                  Send(p);
  555.                  OBuffer := '/W     - To find who else is connected' + CR;
  556.                  Send(p);
  557.  
  558.                End;
  559.  
  560.   'n','N' : Begin
  561.  
  562.               Assign(Users,'Chatuser.dat');
  563.               Reset(Users);
  564.               With Use_data do
  565.               Begin
  566.                 match := false;
  567.                 While (not match) and (not EOF(users)) do
  568.                 Begin
  569.                   Read(Users,Use_data);
  570.                   Match := (User_call=Callsign[p]);
  571.                 End;
  572.  
  573.                 I := Pos(#$0D,IBuffer);
  574.                 User_name := Copy(IBuffer,4,I-4);
  575.                 User_call := Callsign[p];
  576.                 Write(Users,Use_data);
  577.                 OBuffer := 'Hello ' + User_name
  578.                                     + ' thanks for registering' + CR;
  579.                 Send(p);
  580.                 Name[p] := User_name;
  581.               End; { With Use_data }
  582.               Close(Users);
  583.             End;
  584.  
  585.    'q','Q': Begin
  586.               OBuffer := 'Thank you for calling ' + name[p] + CR;
  587.               Send(p);
  588.               Delay(1000);
  589.  
  590.               regs.cx := 2;
  591.               regs.ah := $06;
  592.               regs.al := Start_port + p;
  593.               intr($7F,regs);
  594.             End;
  595.  
  596.   'w','W' : Begin
  597.               OBuffer := 'List of current users ' + CR;
  598.               Send(p);
  599.               For I := 0 to No_ports - 1 do
  600.               Begin
  601.                 If Logged_in[I] then
  602.                 Begin
  603.                   Str(I,Sbit);
  604.                   Str(Conf[I],Cbit);
  605.                   OBuffer := Callsign[I] + '  ' + name[I] +
  606.                        ' connected on port ' + Sbit + ' to conference ' +
  607.                        Cbit + CR;
  608.                   Send(p);
  609.                 End;
  610.               End;
  611.             End;
  612.             else
  613.             Begin
  614.               OBuffer := 'Command not known';
  615.               Send(p);
  616.             End;
  617.     End;  {Case end}
  618.  
  619. End;
  620.  
  621. {***************************  Start of main  ******************************}
  622. Begin
  623.  
  624.   DirectVideo := False;             { Write to screen using BIOS calls }
  625.   Net_connect := False;
  626.  
  627.   ClrScr;
  628.   xkeep := 1;
  629.   ykeep := 1;
  630.   xloc := 1;
  631.   yloc := 1;
  632.  
  633.   For I := 1 to 255 do
  634.     BPQbuff[I] := 0;
  635.  
  636.   GotoXY(1, 4); For I := 1 to 80 do Write('-');
  637.   GotoXY(1,22); For I := 1 to 80 do Write('-');
  638.   GoToXY(1,24); Write('/C - to close down node    /Q - to chop node');
  639.  
  640.   Display(' IMB Chat node'+CRLF);
  641.  
  642.   If not BPQ_loaded then
  643.   Begin
  644.     Display('Version 4 BPQ node not loaded ');
  645.     Halt;
  646.   End;
  647.  
  648.   Get_config;
  649.  
  650.   setup;
  651.  
  652.   For I := 0 to No_ports - 1 do
  653.       LogOut(I);
  654.  
  655.   Quit := false;
  656.   locbuff := '';
  657.  
  658.   Repeat
  659.     Repeat
  660.       For I := 0 to No_ports - 1 do
  661.       Begin
  662.         If (Poll(Start_port+I)) then
  663.            If regs.cx <> 0 then Login(I)
  664.                            else Logout(I);
  665.  
  666.         If Get_resp(Start_port+I) then
  667.            If IBuffer[1] = '/' then Command(I)
  668.                                else Sendall(I);
  669.       End;
  670.  
  671.       If (Poll(Link_port)) then
  672.          If regs.cx <> 0 then Link_Login
  673.                          else Link_Logout;
  674.  
  675.       DV_Nice;
  676.  
  677.     Until Keypressed;
  678.  
  679.     Ch := Readkey;
  680.  
  681.     Case Ch of
  682.     #00 : Begin     { Special keys }
  683.  
  684.           End;
  685.  
  686.     #08 : Begin
  687.             xloc := xloc - 1;
  688.             Delete(locbuff,length(locbuff),1);
  689.             GotoXY(xloc,yloc); Write(' ');
  690.             GoToXY(xloc,yloc);
  691.           End;
  692.  
  693.     #$0D : Begin
  694.              locbuff := locbuff + Ch;
  695.              xloc := 1;
  696.  
  697.              If locbuff[1] = '/' then
  698.              Begin
  699.                Case locbuff[2] of
  700.                '0'..'9' : Begin           { Send a message to just 1 station}
  701.                             p := Ord(locbuff[2]) - $30;
  702.                             Locbuff[1] := '*';
  703.                             Locbuff[2] := '>';
  704.                             OBuffer := '<* sysop '+locbuff;
  705.                             Send(p);
  706.                           End;
  707.  
  708.                'c','C'  : Begin           { Polite close down of node }
  709.                             Shut_down;
  710.                             Delay(2000);
  711.                             Quit := True;
  712.                             End;
  713.  
  714.                'q','Q'  : Quit := True;
  715.  
  716.                End;    { case }
  717.              end       { If / }
  718.              else
  719.              Begin
  720.                IBuffer := Locbuff + CRLF;    { Load it into Ibuffer to be sent out }
  721.                Sendall(10);
  722.              End;
  723.  
  724.              locbuff := '';           { Clear local buffer }
  725.            end; {#0D}
  726.       else
  727.       begin
  728.         GotoXY(xloc,yloc);Write(Ch);
  729.         locbuff := locbuff + Ch;
  730.         xloc := xloc + 1;
  731.       end;
  732.     end;  {Case}
  733.  
  734.     xloc := WhereX;
  735.     yloc := WhereY;
  736.  
  737.   Until Quit;
  738.  
  739.   For I := 0 to No_ports - 1 do
  740.   Begin
  741.     regs.dl := $00;                  { Set application flag to 0 }
  742.     regs.ah := $01;
  743.     regs.al := Start_port + I;
  744.     intr($7F,regs);
  745.   End;
  746.  
  747. end.
  748.